/*    Copyright 2010 Tobias Marschall
 *
 *    This file is part of MoSDi.
 *
 *    MoSDi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    MoSDi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoSDi.  If not, see <http://www.gnu.org/licenses/>.
 */

package mosdi.distributions;

import mosdi.util.Distributions;
import mosdi.util.Log;

/** Precomputes all x-fold convolutions up to a given x. */
public class FullCacheDistributionConvolver implements DistributionConvolver {
	
	double[][] distributionCache;
	double[][] ccdfCache;
	
	public FullCacheDistributionConvolver(double[] distribution, int maxConvolution, boolean cacheDistributions, boolean cacheCCDFs) {
		Log.startTimer();
		if (cacheDistributions) {
			distributionCache = new double[maxConvolution+1][];
			distributionCache[1] = distribution;
		}
		if (cacheCCDFs) {
			ccdfCache = new double[maxConvolution+1][];
			ccdfCache[1] = Distributions.toCCDF(distribution);
		}
		double[] prev = distribution;
		long n = distribution.length;
		for (int i=2; i<=maxConvolution; ++i) {
			double[] current = Distributions.convolve(prev, distribution);
			if (distributionCache!=null) distributionCache[i] = current;
			if (ccdfCache!=null) ccdfCache[i] = Distributions.toCCDF(current);
			prev = current;
			n+=current.length;
		}
		Log.stopTimer("Convolving distribution.");
		Log.printf(Log.Level.DEBUG, "Size of convolved distributions table: %,d bytes.\n", n*4);
	}
	
	@Override
	public double[] getCCDF(int selfConvolution) {
		if (ccdfCache==null) throw new IllegalStateException("CCDFs have not been cached");
		if (selfConvolution>=ccdfCache.length) throw new IllegalArgumentException("Argument too large.");
		return ccdfCache[selfConvolution];
	}

	@Override
	public double[] getDistribution(int selfConvolution) {
		if (distributionCache==null) throw new IllegalStateException("Distributions have not been cached");
		if (selfConvolution>=distributionCache.length) throw new IllegalArgumentException("Argument too large.");
		return distributionCache[selfConvolution];
	}

	@Override
	public double getPValue(int selfConvolution, int value) {
		double[] ccdf = getCCDF(selfConvolution);
		if (value>=ccdf.length) return 0.0;
		return ccdf[value];
	}

}
